﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//xhk ljj 6.14
//战斗物体识别，搜索攻击范围内敌人
public class BattleObject : MonoBehaviour
{
    public string battleObjectName = "";
    public Identity id = Identity.aibo;
    public float searchRange = 2.0f;
    public float maxHP = -1;
    public float detectTime = 0.1f;//索敌刷新频率
    public int maxTargetCount = 1;//最大索敌数量

    public float coolTime;
    public float frontActionTime;
    public float attackActionTime;
    public float afterActionTime;
    public int attackNumber = 1;

    public AttackMode attackMode;
    public AttackType attackType;
    public float aoeRange = 1.0f;
    public float damage = 5.0f;

    public int stopAttack = 0;
    public bool actionBetweenMultipleAttack = true;
    public bool stopWalkWhenLock = false;
    public bool rotatable = false;
    public bool mirrorable = true;
    public bool autoAttack = true;
    public bool hasAttackAnimator = true;
    public bool missileRandomRotation = false;

    public Vector3 missileOffset;
    public BuffAdder buffAdder;

    public GameObject missilePrefab;
    public GameObject parabolaPrefab;
    public GameObject attackEffectPrefab;
    public GameObject hitEffectPrefab;
    public SoldierData soldierData;

    private bool playerActiveAttack = false;
    private BaseAttack[] attack;
    private AttackActionState state;
    private int lastAttackNum;
    private float stateTime;
    private List<GameObject> targets;

    private Animator animator;
    private bool isAttacking;
    private bool isMirror;
    private float hasDamage = 0;
    private float score = 0;

    float searchDeltatime;
    Collider2D body;//可被检测为可攻击目标
    Collider2D[] temp;

    //Rigidbody2D rg;
    HitPoint hitPoint;

    private void Awake()
    {
       
        body = gameObject.GetComponent<Collider2D>();
        targets = new List<GameObject>();
        searchDeltatime = 0;

        isAttacking = false;
        isMirror = false;
        animator = gameObject.GetComponentInChildren<Animator>();
        buffAdder = gameObject.GetComponentInChildren<BuffAdder>();
        UpdateAnimatorSpeed();

    }

    void Start()
    {
        ChangeState(AttackActionState.Ready);
        lastAttackNum = attackNumber;
        if (maxHP != -1 && hitPoint == null)
        {
            hitPoint = gameObject.AddComponent<HitPoint>();
            hitPoint.setMaxHP(maxHP);
            hitPoint.setHP(maxHP);
        }
    }

    // Update is called once per frame
    void Update()
    {
        //索敌
        UpdateTarget();
        UpdateAction();
        
        addLifeTimeScore(Time.deltaTime);
    }

    void UpdateTarget()
    {
        if (maxTargetCount == 0)
            return;
        //判断现在是否有目标以及目标是否有丢失
        if (targets != null && targets.Count == maxTargetCount)
        {
            bool miss = false;
            for (int i = 0; i < targets.Count; i++)
            {
                if (miss = isTargetMiss(targets[i]))
                    break;
            }
            if (!miss)
                return;
        }
        searchDeltatime += Time.deltaTime;
        if (searchDeltatime >= detectTime)
        {
            //Debug.Log(gameObject.name + " search");
            temp = Physics2D.OverlapCircleAll(gameObject.transform.position, searchRange * ConstOfMap.xGap, Function.GetTargetLayerMask(id));
            targets.Clear();
            if (temp.Length > 0)
            {
                List<GameObject> list = new List<GameObject>();
                for (int i = 0; i < temp.Length; i++)
                {
                    if (isVisible(temp[i].gameObject))
                    {
                        list.Add(temp[i].gameObject);
                    }
                }
                while (targets.Count < maxTargetCount || maxTargetCount == -1)
                {
                    int maxIndex = -1;
                    float maxScore = -1;
                    for (int i = 0; i < list.Count; i++)
                    {
                        BattleObject bt = list[i].GetComponent<BattleObject>();
                        if (bt == null)
                        {
                            list.RemoveAt(i);
                            --i;
                            continue;
                        } else
                        {
                            if (bt.score > maxScore)
                            {
                                maxScore = bt.score;
                                maxIndex = i;
                            }
                        }
                    }
                    if (maxIndex >= 0)
                    {
                        targets.Add(list[maxIndex].gameObject);
                        list.RemoveAt(maxIndex);
                    }
                    else
                        break;
                }           
            }
            searchDeltatime = 0;
        }
    }

    public void setEnemy()
    {
        id = Identity.enemy;
    }

    public void setAibo()
    {
        id = Identity.aibo;
    }

    public Identity getId()
    {
        return id;
    }

    bool isTargetMiss(GameObject target)
    {
        if (target == null)
            return true;
        if (Function.DistanceIgnoreZ(gameObject.transform.position, target.transform.position) > searchRange * ConstOfMap.xGap || !isVisible(target))
            return true;
        BattleObject bt = target.GetComponent<BattleObject>();
        if (bt == null || !bt.enabled)
            return true;
        return false;
    }

    public float getMaxHP()
    {
        return this.maxHP;
    }

    public void setMaxHP(float maxHP)
    {
        this.maxHP = maxHP;
        if (hitPoint == null && maxHP !=-1)
            hitPoint = gameObject.AddComponent<HitPoint>();
        hitPoint.setMaxHP(maxHP);
    }

    public float getHP()
    {
        if (hitPoint == null)
            return -1;
        return hitPoint.getCurrentHP();
    }

    public void setHP(float HP)
    {
        hitPoint.setHP(HP);
    }

    public void changeMaxHP(float hp)
    {
        hitPoint.changeMaxHP(hp);
    }

    public void repair()
    {
        if (hitPoint == null)
            return;
        hitPoint.FullHp();
    }


    public void attacked(float damage)
    {
        this.hitPoint.attacked(damage);
    }

    public void setMaxTarger(int maxTargetCount)
    {
        this.maxTargetCount = maxTargetCount;
    }

    bool isVisible(GameObject target)//检测是否可见
    {
        RaycastHit2D[] hits = Physics2D.LinecastAll(transform.position, target.transform.position, LayerMask.GetMask("Cube"));
        //Debug.Log(hits.Length);
        return hits.Length == 0;
    }


    void UpdateAction()
    {

        stateTime += Time.deltaTime;

        //失去目标后
        if (targets == null || targets.Count == 0)
        {
            if (state == AttackActionState.Attacking)
                ChangeState(AttackActionState.After);
            else if (state == AttackActionState.Front)
                ChangeState(AttackActionState.Ready);
        }

        if (state == AttackActionState.Cooling && stateTime >= coolTime)
        {
            ChangeState(AttackActionState.Ready);
            lastAttackNum = attackNumber;
        }

        if (state != AttackActionState.Ready && playerActiveAttack)
            MessageController.Instance.SendMsg("技能冷却中");

        if (state == AttackActionState.Ready && targets.Count == 0 && playerActiveAttack)
            MessageController.Instance.SendMsg("攻击范围内没有目标");    

        if (state == AttackActionState.Ready && targets.Count > 0 && (autoAttack || playerActiveAttack) && (stopAttack <= 0))
            ChangeState(AttackActionState.Front);
        playerActiveAttack = false;

        if (state == AttackActionState.Front && stateTime >= frontActionTime)
        {
            --lastAttackNum;
            ChangeState(AttackActionState.Attacking);
            if (attackEffectPrefab != null)
                EffectObjectManager.Instance.CreateEffectObject(attackEffectPrefab, transform.position, transform.rotation);
        }
        if (state == AttackActionState.Attacking)
        {
            //结算持续伤害
            if (attackMode == AttackMode.Continued)
            {
                LockTargets(damage * Time.deltaTime);
                NormalAttack();
            }

            if (stateTime >= attackActionTime)
            {
                //结算单次伤害
                if (attackMode == AttackMode.Immediate)
                {
                    LockTargets(damage);
                    NormalAttack();
                }
                else if (attackMode == AttackMode.Missile)
                    MissileAttack();
                else if (attackMode == AttackMode.Parabola)
                    ParabolaAttack();
                if (!actionBetweenMultipleAttack && lastAttackNum > 0)
                {
                    --lastAttackNum;
                    ChangeState(AttackActionState.Attacking);
                }       
                else
                    ChangeState(AttackActionState.After);
            }
        }
        if (state == AttackActionState.After && stateTime >= afterActionTime)
        {
            if (lastAttackNum > 0)
                ChangeState(AttackActionState.Ready);
            else
                ChangeState(AttackActionState.Cooling);
        }
        isAttacking = state == AttackActionState.Front ||
            state == AttackActionState.Attacking ||
            state == AttackActionState.After;
        if (isAttacking && targets != null && targets.Count > 0 && targets[0] != null)
        {
            Vector3 forward = targets[0].transform.position - transform.position;
            if (mirrorable)
                UpdateMirrorParam(forward.x < 0);
            if (rotatable)
            {
                float angle = Mathf.Atan2(forward.y, forward.x) * Mathf.Rad2Deg;
                float xscale = (Mathf.Abs(Mathf.Abs(angle) - 90) + 180) / 270.0f;
                if (isMirror)
                {
                    angle = 180f + angle;
                }
                animator.transform.rotation = Quaternion.Euler(0, 0, angle);
                Vector3 scale = animator.transform.localScale;
                if (scale.x < 0)
                    scale.x = -xscale;
                else
                    scale.x = xscale;
                animator.transform.localScale = scale;
            }
        }
    }

    public void ChangeState(AttackActionState state)
    {
        //Debug.Log(gameObject.name + state.ToString());
        this.state = state;
        stateTime = 0;
        if (animator == null)
            return;
        if (!hasAttackAnimator)
            return;
        if (state == AttackActionState.Ready || state == AttackActionState.Cooling)
            animator.SetInteger("State", 0);
        if (state == AttackActionState.Front)
            animator.SetInteger("State", 1);
        if (state == AttackActionState.Attacking)
            animator.SetInteger("State", 2);
        if (state == AttackActionState.After)
            animator.SetInteger("State", 3);
    }

    public void UpdateAnimatorSpeed()
    {
        if (animator == null)
            return;
        AnimationClip[] clips = animator.runtimeAnimatorController.animationClips;
        for (int i = 0; i < clips.Length; i++)
        {
            AnimationClip clip = clips[i];
            float clipLength = clip.length;

            if (clip.name.EndsWith("Front"))
                animator.SetFloat("FrontSpeed", clipLength / frontActionTime);
            if (clip.name.EndsWith("Attack"))
                animator.SetFloat("AttackSpeed", clipLength / attackActionTime);
            if (clip.name.EndsWith("After"))
                animator.SetFloat("AfterSpeed", clipLength / afterActionTime);
        }
    }

    public void UpdateMirrorParam(bool isMirror)
    {
        if (this.isMirror != isMirror)
        {
            this.isMirror = isMirror;
            Vector3 scale = animator.transform.localScale;
            scale.x *= -1f;
            animator.transform.localScale = scale;
        }
    }

    void LockTargets(float damage)
    {
        if (targets.Count == 0)
        {
            attack = null;
            return;
        }
        if (attackType == AttackType.Single)
            attack = new SingleAttack[targets.Count];
        else
            attack = new AoeAttack[targets.Count];
        for (int i = 0; i < targets.Count; i++)
        {
            GameObject target = targets[i];
            if (target != null)
            {
                if (attackType == AttackType.Single)
                    attack[i] = new SingleAttack(gameObject, target, damage);
                else
                    attack[i] = new AoeAttack(gameObject, target.transform.position, aoeRange * ConstOfMap.xGap, damage, id);
                if (buffAdder != null)
                    attack[i].buffAdderOnAttack.buffList = buffAdder.buffList;
            }
            else
                attack[i] = null;
        }
    }
   
    void NormalAttack()
    {
        if (attack == null)
            return;
        for (int i = 0; i < attack.Length; i++)
            if (attack[i] != null)
            {
                attack[i].Attack();
                if (hitEffectPrefab != null)
                {
                    GameObject hitEffectObject = EffectObjectManager.Instance.CreateEffectObject(hitEffectPrefab, attack[i].GetTargetPos(), Quaternion.identity);
                }
            }
    }

    void MissileAttack()
    {
        if (targets == null)
            return;
        for (int i = 0; i < targets.Count; i++)
        {
            if (targets[i] == null)
                continue;
            GameObject target = targets[i];

            float angle = Function.GetDegIgnoreZ(target.transform.position, animator.transform.position);
            Vector3 offset;
            if (rotatable)
            {
                if (isMirror)
                {
                    Vector3 tmpOffset = missileOffset;
                    tmpOffset.x *= -1;
                    angle += 180;
                    offset = Quaternion.Euler(0, 0, angle) * tmpOffset;
                }
                else
                {
                    offset = Quaternion.Euler(0, 0, angle) * missileOffset;
                }
            }
            else
                offset = missileOffset;


            Vector3 position = animator.transform.position + offset;
            Quaternion rotation = Quaternion.identity;

            GameObject missileObject = EffectObjectManager.Instance.CreateEffectObject(missilePrefab, position, rotation);
            //    GameObject.Instantiate(missilePrefab, position, rotation, transform.parent);
            missileObject.layer = LayerMask.NameToLayer("Missile");
            Missile missile = missileObject.GetComponent<Missile>();
            if (hitEffectPrefab != null)
            {
                missile.hitEffectPrefab = hitEffectPrefab;
            }
           
            if (attackType == AttackType.Aoe)
                missile.attack = new AoeAttack(gameObject, Vector3.zero, aoeRange, damage, id);
            else
                missile.attack = new SingleAttack(gameObject, targets[i], damage);

            missile.target = targets[i];
            if (buffAdder != null)
                missile.attack.buffAdderOnAttack.buffList = buffAdder.buffList;

            if (!missileRandomRotation)
                missile.InitForward();
            else
            {
                missile.forwardRad = RandomGenerator.Instance.Next(360) * Mathf.Deg2Rad;
                missile.UpdataForward();
            }
        }
    }

    void ParabolaAttack()
    {
        if (targets == null)
            return;
        for (int i = 0; i < targets.Count; i++)
        {
            if (targets[i] == null)
                continue;
            GameObject target = targets[i];

            GameObject parabolaObject = EffectObjectManager.Instance.CreateEffectObject(parabolaPrefab, transform.position, Quaternion.identity);
            //GameObject.Instantiate(parabolaPrefab, transform.position, Quaternion.identity, transform.parent);
            parabolaObject.layer = LayerMask.NameToLayer("Missile");
            Parabola parabola = parabolaObject.GetComponent<Parabola>();
            if (hitEffectPrefab != null)
            {
                parabola.hitEffectPrefab = hitEffectPrefab;
            }
            parabola.startPos = transform.position;
            parabola.targetPos = target.transform.position;
            parabola.attack = new AoeAttack(gameObject, Vector3.zero, aoeRange, damage, id);

            if (buffAdder != null)
                parabola.attack.buffAdderOnAttack.buffList = buffAdder.buffList;
        }
    }

    public bool CanWalk()
    {
        bool ret = !this.isAttacking;
        if (stopWalkWhenLock)
            ret &= (targets == null || targets.Count == 0);
        return ret;
    }

    public void PlayerActiveAttack()
    {
        playerActiveAttack = true;
    }

    public void addLifeTimeScore(float x)
    {
        score += x * ConstOfGenetic.weightOfLifeTime;
    }

    public void addDamageScore(float x)
    {
        hasDamage += x;
        score += x * ConstOfGenetic.weightOfDamage;
    }

    public void addGoalScore(float x)
    {
        score += x * ConstOfGenetic.weightOfCatchGoal;
    }

    public int GetScore()
    {
        return (int)score;
    }
    /*
    public void OnDestroy()
    {
        if (soldierData == null)
            return;
        soldierData.score = (int)score;
        Debug.Log("score=" + score.ToString());
        Genetic.Instance.addSoldier(soldierData);
    }
    */
    public List<BuffInfo> GetAllBuff()
    {
        BaseBuff[] objs = gameObject.GetComponents<BaseBuff>();
        //Debug.Log(gameObject.name + " has " + objs.Length + " buff");
        List<BuffInfo> ret = new List<BuffInfo>();
        for (int i = 0; i < objs.Length; i++)
        {
            int buffIndex = 0;
            float effect = objs[i].effect;
            float duration = objs[i].duration;
            buffIndex = (int)System.Enum.Parse(typeof(ConstOfBuff.BuffType), objs[i].GetType().ToString());
            BuffInfo item = new BuffInfo(buffIndex, effect, duration);
            ret.Add(item);
        }
        return ret;
    }

    public void SetBuff(BuffInfo buffInfo)
    {
        BuffAdderOnAttack buffAdder = new BuffAdderOnAttack();
        buffAdder.buffList.Add(buffInfo);
        buffAdder.AddBuffToObj(gameObject);
        AwakeBuff();
    }

    public void SetAllBuff(List<BuffInfo> lists)
    {
        BuffAdderOnAttack buffAdder = new BuffAdderOnAttack();
        buffAdder.buffList = lists;
        buffAdder.AddBuffToObj(gameObject);
        AwakeBuff();
    }

    public void AwakeBuff()
    {
        BaseBuff[] buffs = gameObject.GetComponents<BaseBuff>();
        for (int i = 0; i < buffs.Length; i++)
            buffs[i].AwakeNow();
    }

    public int GetLevel()
    {
        LevelUp levelBuff = gameObject.GetComponent<LevelUp>();
        if (levelBuff == null)
            return 0;
        return (int)levelBuff.effect;
    }

    public void LevelUp()
    {
        float level = GetLevel() + 1;
        SetBuff(new BuffInfo(2, level, -1));
        repair();
    }

}
